import { Cache, WebGLRenderer, PerspectiveCamera, OrthographicCamera, Scene, Color, AxesHelper, GridHelper, Vector2, Raycaster } from "three"
// import { CSS2DRenderer } from "three/addons/renderers/CSS2DRenderer.js" // 二维标签渲染器
import { CSS3DRenderer } from "three/addons/renderers/CSS3DRenderer.js" // 三维标签渲染器
import { OrbitControls } from "three/addons/controls/OrbitControls.js"
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import Stats from "three/examples/jsm/libs/stats.module"
import SkyBoxs from "./SkyBoxs"
import Lights from "./Lights"
import ThreeMouseEvent from "./Events/ThreeMouseEvent"

export default class Viewer {
  /**
   * 
   * @param {*} dom 场景容器dom
   */
  constructor(dom) {
    Cache.enabled = true // 开启缓存
    this.viewerDom = dom
    this.renderer = undefined
    this.scene = undefined
    this.cameraPersp = undefined
    this.cameraOrtho = undefined
    this.camera = undefined
    this.orbitControl = undefined
    // 网格辅助线
    this.gridHelper = undefined
    // 辅助对象：坐标轴
    this.axesHelper = undefined
    // 动画帧
    this.animationFrame = undefined
    this.animateEventList = []
    // 二维标签
    // this.labelRenderer = null
    // 三维标签
    this.css3DRenderer = null
    // 效果合成器
    this.effectComposer
    this.outlinePass
    // 模型，管道
    this.objects = []
    this.pipes = []
    // 碰撞检测
    this.raycaster = new Raycaster()
    // 鼠标位置
    this.mouse = new Vector2()
    this.#initViewer()
  }

  #initViewer() {
    this.#initRenderer()
    this.#initCamera()
    this.#initScene()
    this.#initControl()
    this.#initSkybox()
    this.#initLight()
    // 创建效果合成器
    // this.createEffectComposer()
    // 设置模型轴/辅助线信息
    this.setModelAxleLine()

    const animate = () => {
      this.animationFrame = requestAnimationFrame(animate)
      this.#updateDom()
      this.#renderDom()
      // 全局的公共动画函数，添加函数可同步执行
      this.animateEventList.forEach(
        event => {
          event.fun && event.content && event.fun(event.content)
        })
      // this.effectComposer.render()
    }

    animate()
  }
  /**
     * 添加坐标轴
     */
  addAxis() {
    // 显示坐标轴(x轴: 红色; y轴: 绿色; z轴: 蓝色)
    // x轴水平方向(右正); y轴垂直方向(上正); z轴垂直xy平面即屏幕(外正)
    this.axesHelper = new AxesHelper(100)
    this.scene.add(this.axesHelper)
  }
  /**
   * 添加状态检测
   */
  addStats() {
    if (!this.statsControls) {
      this.statsControls = new Stats()
    }
    this.statsControls.dom.style.position = 'absolute'
    this.viewerDom.appendChild(this.statsControls.dom)
    // 添加到动画
    this.statsUpdateObj = {
      fun: this.#statsUpdate,
      content: this.statsControls
    }
    this.addAnimate(this.statsUpdateObj)
  }
  /**
   * 移除状态检测
   */
  removeStats() {
    if (this.statsControls && this.statsUpdateObj) {
      this.viewerDom.removeChild(this.statsControls.dom)
      this.removeAnimate(this.statsUpdateObj)
    }
  }
  #statsUpdate(statsControls) {
    statsControls.update()
  }

  /**
   * 更新DOM
   */
  #updateDom() {
    this.orbitControl.update();

    const aspect = this.viewerDom.clientWidth / this.viewerDom.clientHeight // 摄像机视锥体的长宽比，通常是使用画布的宽/画布的高
    this.cameraPersp.aspect = aspect;
    this.cameraPersp.updateProjectionMatrix() // 更新摄像机投影矩阵 在任何参数被改变以后必须被调用,来使得这些改变生效
    this.cameraOrtho.left = this.cameraOrtho.bottom * aspect;
    this.cameraOrtho.right = this.cameraOrtho.top * aspect;
    this.cameraOrtho.updateProjectionMatrix();

    this.renderer.setSize(this.viewerDom.clientWidth, this.viewerDom.clientHeight) // 设置渲染器的尺寸
    this.renderer.setPixelRatio(window.devicePixelRatio) // 设置渲染器的像素比
    // this.labelRenderer.setSize(this.viewerDom.clientWidth, this.viewerDom.clientHeight) // 设置标签渲染器的尺寸
    this.css3DRenderer.setSize(this.viewerDom.clientWidth, this.viewerDom.clientHeight) // 设置标签渲染器的尺寸
  }
  #renderDom() {
    this.renderer.render(this.scene, this.camera) // 渲染场景
    // this.labelRenderer.render(this.scene, this.camera) // 渲染2d标签场景
    this.css3DRenderer.render(this.scene, this.camera) // 渲染3d标签场景
  }

  /**
   * 创建初始化场景界面
   */
  #initRenderer() {
    // 初始化渲染器
    this.renderer = new WebGLRenderer({
      // logarithmicDepthBuffer: true, // true/false 表示是否使用对数深度缓冲，true性能不好
      antialias: true, // true/false表示是否开启反锯齿
      alpha: true, // true/false 表示是否可以设置背景色透明
      precision: "highp", // highp/mediump/lowp 表示着色精度选择
      premultipliedAlpha: true, // true/false 表示是否可以设置像素深度（用来度量图像的分辨率）
    })
    this.renderer.clearDepth(); // 设置深度缓冲区
    this.renderer.shadowMap.enabled = true // 场景中的阴影自动更新
    // this.renderer.setClearColor(0xb9d3ff, 1); // 设置背景颜色
    this.viewerDom.appendChild(this.renderer.domElement) // 将渲染器添加到画布中
    // // 二维标签
    // this.labelRenderer = new CSS2DRenderer() // 标签渲染器
    // this.labelRenderer.domElement.style.zIndex = 2
    // this.labelRenderer.domElement.style.position = 'absolute'
    // this.labelRenderer.domElement.style.top = '0px'
    // this.labelRenderer.domElement.style.left = '0px'
    // this.labelRenderer.domElement.style.pointerEvents = 'none'// 避免HTML标签遮挡三维场景的鼠标事件
    // this.viewerDom.appendChild(this.labelRenderer.domElement)

    // 三维标签
    this.css3DRenderer = new CSS3DRenderer() // 标签渲染器
    this.css3DRenderer.domElement.style.zIndex = 0
    this.css3DRenderer.domElement.style.position = 'absolute'
    this.css3DRenderer.domElement.style.top = '0px'
    this.css3DRenderer.domElement.style.left = '0px'
    this.css3DRenderer.domElement.style.pointerEvents = 'none'// 避免HTML标签遮挡三维场景的鼠标事件
    this.viewerDom.appendChild(this.css3DRenderer.domElement)
  }

  /**
   * 渲染相机
   */
  #initCamera() {
    // this.camera = new PerspectiveCamera(45, this.viewerDom.clientWidth / this.viewerDom.clientHeight, 0.1, 500000) // 透视相机

    const k = this.viewerDom.clientWidth / this.viewerDom.clientHeight; // 窗口宽高比
    const s = 62; // 三维场景显示范围控制系数，系数越大，显示的范围越大
    this.cameraPersp = new PerspectiveCamera(30, k, 0.1, 10000);
    this.cameraOrtho = new OrthographicCamera(
      -s * k,
      s * k,
      s,
      -s,
      0.1,
      10000
    );
    this.camera = this.cameraPersp;
    this.cameraPersp.minPolarAngle = Math.PI / 4; // 最小极角为 45 度
    this.cameraPersp.maxPolarAngle = Math.PI / 2; // 最大极角为 90 度

    this.camera.position.set(50, 0, 50) // 相机位置
    this.camera.lookAt(0, 0, 0) // 设置相机方向
  }

  // 切换相机
  changeCamera(fun) {
    const position = this.camera.position.clone();

    this.camera = this.camera.isPerspectiveCamera
      ? this.cameraOrtho
      : this.cameraPersp;
    this.camera.position.copy(position);

    this.orbitControl.object = this.camera;
    // transformControl.camera = currentCamera;
    fun?.()

    this.camera.lookAt(
      this.orbitControl.target.x,
      this.orbitControl.target.y,
      this.orbitControl.target.z
    );

    if (this.skyboxs) {
      if (this.camera.isPerspectiveCamera) {
        this.skyboxs.setSkybox()
      } else {
        this.skyboxs.removeSkybox()
        this.renderer.setClearColor(0x82baf8, 1); // 设置背景颜色
      }
    }

    this.#updateDom()
  }

  // 模型轴辅助线配置
  setModelAxleLine() {
    //网格辅助线
    this.gridHelper = new GridHelper(600, 50, '#fff', 'rgb(193,193,193)');
    this.gridHelper.visible = true
    this.gridHelper.material.linewidth = 0.1
    this.scene.add(this.gridHelper)
  }

  /**
   * 渲染场景
   */
  #initScene() {
    this.scene = new Scene()
    this.scene.background = new Color('rgb(5,24,38)')
  }

  /**
   * 初始化控制
   */
  #initControl() {
    this.orbitControl = new OrbitControls(this.camera, this.renderer.domElement)
    this.orbitControl.enableDamping = false // 是否开启阻尼
    this.orbitControl.screenSpacePanning = false // 定义当平移的时候摄像机的位置将如何移动, 摄像机将在与摄像机向上方向垂直的平面中平移
  }

  /**
   * 初始化天空盒
   */
  #initSkybox() {
    if (!this.skyboxs) {
      this.skyboxs = new SkyBoxs(this)
    }
    this.skyboxs.createTexture()
    this.skyboxs.setSkybox()
  }

  /**
   * 初始化灯光
   */
  #initLight() {
    if (!this.lights) {
      this.lights = new Lights(this)
    }
  }

  // 创建效果合成器
  createEffectComposer() {
    const { clientHeight, clientWidth } = this.viewerDom
    this.effectComposer = new EffectComposer(this.renderer)
    const renderPass = new RenderPass(this.scene, this.camera)
    this.effectComposer.addPass(renderPass)
    this.outlinePass = new OutlinePass(new Vector2(clientWidth, clientHeight), this.scene, this.camera)
    this.outlinePass.visibleEdgeColor = new Color('#FF8C00') // 可见边缘的颜色
    this.outlinePass.hiddenEdgeColor = new Color('#8a90f3') // 不可见边缘的颜色
    this.outlinePass.edgeGlow = 2.0 // 发光强度
    this.outlinePass.edgeThickness = 1 // 边缘浓度
    this.outlinePass.edgeStrength = 4 // 边缘的强度，值越高边框范围越大
    this.outlinePass.pulsePeriod = 200 // 闪烁频率，值越大频率越低
    this.effectComposer.addPass(this.outlinePass)
    let outputPass = new OutputPass()
    this.effectComposer.addPass(outputPass)
  }

  /**
     * 添加全局的动画事件
     * @param animate 函数加参数对象
     * 传入对象 = {
            fun: 函数名称,
            content: 函数参数
        }
     */
  addAnimate(animate) {
    this.animateEventList.push(animate)
  }

  /**
    * 移除全局的动画事件
    * @param animate 函数加参数对象
    * 传入对象 = {
           fun: 函数名称,
           content: 函数参数
       }
    */
  removeAnimate(animate) {
    this.animateEventList.map((val, i) => {
      if (val === animate) this.animateEventList.splice(i, 1)
    })
  }

  /**
   * 开启鼠标事件
   * @param {*} mouseType 鼠标类型
   * @param {*} isSelect 是否选中
   * @param {*} callback 鼠标回调
   */
  startSelectEvent(mouseType, isSelect, callback) {
    if (!this.mouseEvent) {
      this.mouseEvent = new ThreeMouseEvent(this, isSelect, callback, mouseType)
    }
    this.mouseEvent.startSelect()
  }

  /**
   * 关闭鼠标事件
   */
  stopSelectEvent() {
    this.mouseEvent?.stopSelect()
  }

  /**
 * 模型位置
 */
calMousePosition (clientX, clientY) {
  const { clientHeight, clientWidth, offsetLeft, offsetTop } = this.viewerDom;
  // 计算鼠标在屏幕上的坐标
  this.mouse.x = ((clientX - offsetLeft) / clientWidth) * 2 - 1;
  this.mouse.y = -((clientY - offsetTop) / clientHeight) * 2 + 1;

  this.raycaster.setFromCamera(this.mouse, this.camera);
  const intersects = this.raycaster.intersectObjects(this.scene.children, true);

  if (intersects.length > 0) {
    const { x, y, z } = intersects[0].point;
    return [x, y, z];
  } else {
    ElMessage.warning("当前角度无法获取鼠标位置请调整“相机角度”在添加");
  }
};

  /**
   * 退出页面
   */
  handleBeforeUnmount() {
    this.#onClearModelData()
  }
  // 清除模型数据
  #onClearModelData() {
    cancelAnimationFrame(this.animationFrame)
    this.scene.traverse((v) => {
      if (v.type === 'Mesh') {
        v.geometry.dispose();
        v.material.dispose();
      }
    })
    this.scene.clear()
    this.renderer.clear()
    this.renderer.dispose()
    // this.camera.clear()
    this.cameraPersp.clear()
    this.cameraOrtho.clear()

    if (this.axesHelper) {
      this.axesHelper.clear()
      this.axesHelper.dispose()
    }
    if (this.skyboxs) {
      this.skyboxs.destorySkybox()
      this.skyboxs = null
    }
    if (this.gridHelper) {
      this.gridHelper.clear()
      this.gridHelper.dispose()
    }
    this.container = null
    // 相机
    this.camera = null
    this.cameraPersp = null
    this.cameraOrtho = null
    // 场景
    this.scene = null
    //渲染器
    this.renderer = null
    // 控制器
    this.orbitControl = null
    // 动画帧
    this.animationFrame = null
    // 坐标轴辅助线
    this.axesHelper = null
    // 二维标签
    // this.labelRenderer = null
    // 3d文字渲染器
    this.css3DRenderer = null
    // 网格辅助线
    this.gridHelper = null

    this.objects = null
    this.pipes = null

    // 碰撞检测
    this.raycaster == null
    // 鼠标位置
    this.mouse = null
  }
} 